import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.BufferedReader;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.Thread.State;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.sql.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import java.util.StringTokenizer;

import com.yahoo.search.SearchClient;
import com.yahoo.search.SearchException;
import com.yahoo.search.WebSearchRequest;
import com.yahoo.search.WebSearchResult;
import com.yahoo.search.WebSearchResults;

public class ThreadWorker{
	protected Vector <Request> requestsQueue;
	Socket clientSocket;
	boolean socketShouldBeClosed = false;
	boolean nonPersistentMode = false;
	boolean timeOut = false;
	Hashtable<String, data_content> cache;
	Thread t1;
	Thread t2;
	boolean waitingForRequest;
	
    // For handling range requests
    byte[] clientData = null;
    boolean clientDataFull = false;
    int contentLength = 0; // downloaded length
    int requestLength = 0; // content length of the requested object
    /////////////////////////////////////////////////////////////////
    	
	public ThreadWorker(Socket s, Hashtable<String, data_content> c){
		clientSocket = s;
		requestsQueue = new Vector<Request>();
		cache = c;
		waitingForRequest = false;
	}
	
	public void runThreadWorker(){
		//System.err.println("--------------------Spawning Threads to handle Request--------------------");
		t1 = new Thread(new RequestHandlingThread());
		t2 = new Thread(new ResponseHandlingThread(cache));
		
		t1.start();
	}
	
	// TODO:
	class RequestHandlingThread implements Runnable {
		BufferedReader inFromClient = null;	// Get message from client
		String requestLineMsg;
		String headerData;
		Request req;
		
		public synchronized void run() {
			System.err.println(Thread.currentThread() + ": requesthandling thread starting");
			
			try {
				inFromClient = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
				boolean successfulHandleRequest = true;
				
				for(;;){
					try {
						successfulHandleRequest = handleRequest();
						
						// If handleRequest not succesful, then there were no requests for the past 60 secs and hence close.
						if(!successfulHandleRequest)
							break;
						
						/*if(!waitingForRequest && !successfulHandleRequest){
							waitingForRequest = true;
							clientSocket.setSoTimeout(60000);
						} else if(waitingForRequest && successfulHandleRequest) {
							waitingForRequest = false;
							clientSocket.setSoTimeout(0);
						}*/
					}  catch (SocketException se) {
						System.err.println("Trying to read from closed socket!");
						break;
					} catch (IOException e) {
						e.printStackTrace();
					} 
				}
			
				//inFromClient.close();
			
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			
			//System.err.println(Thread.currentThread() + ": requestHandling thread ending");
			
		}
		
		public boolean handleRequest() throws IOException, SocketException{
			
			clientSocket.setSoTimeout(120000);
			try {
				req = new Request();
				requestLineMsg = inFromClient.readLine();
				if (requestLineMsg == null) {
					System.err.println("Connection already closed!");
					//if(clientSocket.isClosed())
						throw new SocketException();
				}
				//System.err.println("Request message: " + requestLineMsg);
				if (!socketShouldBeClosed && requestLineMsg != null && requestLineMsg.compareTo("") != 0){
					// Read the rest of the message headers
					headerData = inFromClient.readLine();
		
					//System.err.println(headerData);
					while ( headerData != null
							&& !(headerData.equals("")) 
							&& !(headerData.equals("\r"))
							&& !(headerData.equals("\r\n"))
							&& !(headerData.equals("\n\r") )) {
						req.hashRequest(headerData);
						headerData = inFromClient.readLine();
						
						//System.err.println(headerData);

					}
					
					StringTokenizer tokenizedLine = new StringTokenizer(requestLineMsg);
					
					// it is a bad request if it is not the form of "Request_type URL HTTP_version"
					if (tokenizedLine.countTokens() == 3) {

						String requestOperation = tokenizedLine.nextToken();
						String fileName = tokenizedLine.nextToken();
						String version  =  tokenizedLine.nextToken();
						
						// Add request type, file name, and http version to the requests map
						req.addEntry("Request-Type", requestOperation);
						// File name can be absolute path too.
						req.addEntry("File-Name", fileName);
						req.addEntry("Version",version);
						
						// Add request to the request queue
						requestsQueue.add(req);
						
						System.err.println("Request Map: "+req.getRequestMap());
						System.err.println("==========================  End of the request =======================");
						System.err.println();
						//notify();
						if(t2.getState().equals(State.NEW)) {
							t2.start();
						}
					}else {
						req.addEntry("Bad-Request","Bad-Request");
						requestsQueue.add(req);
					}
					
					return true;
				}
			} catch (SocketTimeoutException se) {
				return false;
			}
			
			return false;

		}
	}

	//  TODO:
	class ResponseHandlingThread implements Runnable {
		Request r;
		DataOutputStream outToClient;
		//Hashtable<String, data_content> cache;
		Socket serverConnection;

		Socket proxyServerConnection;
		DataInputStream serverToProxy;
		DataOutputStream proxyToServer;
		DataOutputStream proxyToClient;
		
		Thread reader;
		Thread writer;
		
		boolean stall;
		int responseNumber;
		
		public ResponseHandlingThread(Hashtable<String, data_content> c) {
			cache = c;
			stall = false;
			responseNumber = 0;
		}
		@Override
		public synchronized void run() {
		// Thread Writer (to socket connection to server)
				// Remove request from queue
					// If request in cache, send conditional get to server for data
					// If request is not in cache, forward get/head request and store in cache
		
			
			// Thread Reader (from socket connection from server)
				// Receive request
					// If the response is a 304 message, then data is not new and send cache data to client
					// Else, record data and store in cache
			
			
			/*if(requestsQueue.isEmpty()) {
				System.err.println(Thread.currentThread() + " (responsehandling thread) is waiting");
				try {
					wait();
					System.err.println(Thread.currentThread() + " (responsehandling thread) is done waiting");
				} catch (InterruptedException e) {
					System.err.println(Thread.currentThread() + " is done waiting and starting to handle data!");
					//e.printStackTrace();
				}
			}*/
			
			
			System.err.println(Thread.currentThread() + ": Response handling thread starting!");
			r = requestsQueue.firstElement();
			String file = r.getRequestHeaderValue("File-Name");
			
			// Check if the file-name starts with http:
			if (file.startsWith("http://")) {
				
				//String sub = file.substring(6);
				StringTokenizer location = new StringTokenizer(file, "/");
				
				location.nextToken();
				String URL = location.nextToken();
				int serverPort = 80;
				String[] URLinfo = URL.split(":");
				if(URLinfo.length == 2)
					serverPort = Integer.parseInt(URLinfo[1]);
				
				try {
					proxyServerConnection = new Socket(URL, serverPort);
					
					serverToProxy = new DataInputStream(new BufferedInputStream(proxyServerConnection.getInputStream()));
					proxyToServer = new DataOutputStream(new BufferedOutputStream(proxyServerConnection.getOutputStream()));
					proxyToClient = new DataOutputStream(new BufferedOutputStream(clientSocket.getOutputStream()));
					
					reader = new Thread(new serverReader(requestsQueue, cache, proxyToServer, clientSocket, proxyServerConnection,serverToProxy));
					writer = new Thread(new serverWriter(requestsQueue, cache, serverToProxy, proxyToClient, clientSocket));
					
					reader.start();
					writer.start();
				} catch (UnknownHostException e) {
					System.out.println("Unknown URL: Connection not set");
					e.printStackTrace();
				} catch (IOException e) {
					System.out.println("Cannot open connection stream");
					e.printStackTrace();
				}

			} else {
				// Probably direct proxy command
			}
			
			System.err.println(Thread.currentThread() + ": Response handling thread ending!");
		}
	
	
		
		// TODO: 
		class serverReader extends ServerConnection {
/*			Hashtable<String, data_content> cache;
			protected Vector <Request> requestsQueue;
			DataOutputStream proxyToServer;
			Socket clientSocket;
			Socket proxyServerConnection;
			DataInputStream serverToProxy;*/
			
			int requestNumber;
			Request req;

			
			public serverReader(Vector <Request> q, Hashtable<String, data_content> c, DataOutputStream pTs, Socket s, Socket pSC, DataInputStream sTp) {
				/*this.cache = c;
				this.requestsQueue = q;
				this.proxyToServer = pTs;
				this.clientSocket = s;
				this.proxyServerConnection = pSC;*/
				requestNumber = 0;

			}
			
			public void run() {
				// Should check queue and remove request. when done with request
				//while(requestsQueue.isEmpty());
				
				System.err.println(Thread.currentThread() + ": serverReader has started");
				String requestType;
				String fileName;
				for(;;) {
					// Check if the socket is closed
					if(clientSocket.isClosed())
						break;
					
					// Check if writer is stalled (waiting for response from closed connection). If stalled, then re-connect to server 
					// and resend request writer is waiting for (and all potential connections after).
					if (stall) {
						if((responseNumber < requestsQueue.size())) {
							
							Request r = requestsQueue.firstElement();
							String file = r.getRequestHeaderValue("File-Name");
							
							// Check if the file-name starts with http:
							if (file.startsWith("http://")) {
								
								//String sub = file.substring(6);
								StringTokenizer location = new StringTokenizer(file, "/");
								
								location.nextToken();
								String URL = location.nextToken();
								int serverPort = 80;
								String[] URLinfo = URL.split(":");
								if(URLinfo.length == 2)
									serverPort = Integer.parseInt(URLinfo[1]);
								
								try {
									proxyServerConnection = new Socket(URL, serverPort);
									
									serverToProxy = new DataInputStream(new BufferedInputStream(proxyServerConnection.getInputStream()));
									proxyToServer = new DataOutputStream(new BufferedOutputStream(proxyServerConnection.getOutputStream()));
									
								} catch (UnknownHostException e) {
									System.out.println("Unknown URL: Connection not set");
									e.printStackTrace();
									System.exit(-1);

								} catch (IOException e) {
									System.out.println("Cannot open connection stream");
									e.printStackTrace();
									System.exit(-1);
								}
								
								System.err.println("Reopening socket connection to " + URL);
							} else {
								System.err.println("Not valid absolute value");
							}
														
							requestNumber = responseNumber;
							req = requestsQueue.get(responseNumber);
							requestNumber++;
							
							stall = false;
							
							// check if the request is valid?
							if (req.getRequestHeaderValue("Bad-Request") == null) {
							
								requestType = req.getRequestHeaderValue("Request-Type");
								fileName = req.getRequestHeaderValue("File-Name");
								
								//check http version
								String version = req.getRequestHeaderValue("Version");
								if (requestType != null && fileName != null && version != null ){
									if (version.compareTo("HTTP/1.1") == 0){
										handleRequest(fileName, requestType);											
										
										synchronized(writer) {
											writer.notify();
										}
									} else if (version.compareTo("HTTP/1.0") == 0){
										notifyClient("Error 400: "+version+" is not supported.");
									} else {
										notifyClient("Error 400: "+version+" is not supported.");
									}
								}else {
									notifyClient("Error 400: Bad request syntax.");
								}
							} else {
								notifyClient("Error 400: Bad request. Please check your request format.");			
							}
						}
					}
					// Check if the connection to server is closed and if there are more request to SEND TO the server. 
					// If it is and there are more request, then try opening a new connection and re-send request
					if(proxyServerConnection.isClosed() && requestNumber < requestsQueue.size()) {
						Request r = requestsQueue.firstElement();
						String file = r.getRequestHeaderValue("File-Name");
						
						StringTokenizer location = new StringTokenizer(file, "/");
						
						location.nextToken();
						String URL = location.nextToken();
						int serverPort = 80;
						String[] URLinfo = URL.split(":");
						if(URLinfo.length == 2)
							serverPort = Integer.parseInt(URLinfo[1]);
						try {
							proxyServerConnection = new Socket(URL, serverPort);
							
							serverToProxy = new DataInputStream(new BufferedInputStream(proxyServerConnection.getInputStream()));
							proxyToServer = new DataOutputStream(new BufferedOutputStream(proxyServerConnection.getOutputStream()));
							
						} catch (UnknownHostException e) {
							System.out.println("Unknown URL: Connection not set");
							e.printStackTrace();
							break;
						} catch (IOException e) {
							System.out.println("Cannot open connection stream");
							e.printStackTrace();
							break;
						}
					} else if (!(requestNumber >= requestsQueue.size())){
						req = requestsQueue.get(requestNumber);
						requestNumber++;
						
						// check if the request is valid?
						if (req.getRequestHeaderValue("Bad-Request") == null) {
						
							requestType = req.getRequestHeaderValue("Request-Type");
							fileName = req.getRequestHeaderValue("File-Name");
							
							//check http version
							String version = req.getRequestHeaderValue("Version");
							if (requestType != null && fileName != null && version != null ){
								if (version.compareTo("HTTP/1.1") == 0){
									handleRequest(fileName, requestType);
									/*if(writer.getState().equals(State.NEW)) {
										writer.start();
									}*/
										
									
								} else if (version.compareTo("HTTP/1.0") == 0){
									notifyClient("Error 400: "+version+" is not supported.");
								} else {
									notifyClient("Error 400: "+version+" is not supported.");
								}
							}else {
								notifyClient("Error 400: Bad request syntax.");
							}
						} else {
							notifyClient("Error 400: Bad request. Please check your request format.");			
						}
					}
				}
			}
			
			public void handleRequest(String fileName, String requestType) {
				
				//Check cache
				data_content entry = cache.get(fileName);
				StringTokenizer location = new StringTokenizer(fileName, "/");
				
				location.nextToken();
				String URL = location.nextToken();
				String[] URLinfo = URL.split(":");
				URL = URLinfo[0];
				
				String requestData = "[UNKNOWN MESSAGE]";
				try {
					if (entry == null) {
						if(requestType.compareTo("GET") == 0) {
							String contentRange = req.getRequestHeaderValue("Range");
							if (contentRange != null)
                                requestData = "GET " + fileName + " HTTP/1.1\r\n" + 
                                                                        "Host: " + URL + "\r\n" +
                                                                        "Range: " + contentRange + "\r\n" +
                                                                        "\r\n";
							else
								requestData = "GET " + fileName + " HTTP/1.1\r\n" + 
												"Host: " + URL + "\r\n" +
												"\r\n";
							writeToServer(requestData);
						} else if (requestType.compareTo("HEAD") == 0) {
							requestData = "HEAD " + fileName + " HTTP/1.1\r\n" + 
													"Host: " + URL + "\r\n" +	
													"\r\n";
							writeToServer(requestData);
						} else {
							notifyClient("Request type not supported: " + requestType);
						}
					} else {
						if(requestType.compareTo("GET") == 0) {
							String contentRange = req.getRequestHeaderValue("Range");
                            if (contentRange != null) {
                            	if(entry.isCompleted()) {
                            		requestData = "GET " + fileName + " HTTP/1.1\r\n" + 
                                                        "Host: " + URL + "\r\n" +
                                                        "Range: " + contentRange + "\r\n" +
                                                        "If-Modified-Since: " + entry.getTimestamp() + "\r\n" + 
                                                        "\r\n";
                            	} else {
                                    requestData = "GET " + fileName + " HTTP/1.1\r\n" + 
                                    					"Host: " + URL + "\r\n" +
                                    					"Range: " + contentRange + "\r\n" +
                                    					"\r\n";
                            	}
                            } else {
                            	requestData = "GET " + fileName + " HTTP/1.1\r\n" + 
												"Host: " + URL + "\r\n" +
												"If-Modified-Since: " + entry.getTimestamp() + "\r\n" + 
												"\r\n";
                            }
							writeToServer(requestData);	
						} else if(requestType.compareTo("HEAD") == 0) {
							requestData = "HEAD " + fileName + " HTTP/1.1\r\n" + 
										"Host: " + URL + "\r\n" +
										"If-Modified-Since: " + entry.getTimestamp() + "\r\n" + 
										"\r\n";
							writeToServer(requestData);	
						} else {
							notifyClient("Request type not supported: " + requestType);
						}
					}
				} catch (IOException e) {
					System.out.println("ERROR! Unable to write data: " + requestData);
				}

			}

			public void writeToServer(String m) throws IOException{
				synchronized(proxyToServer){
					System.err.println("Sending request: "+ m);
					proxyToServer.writeBytes(m);
					proxyToServer.flush();
				}
			}
			public void notifyClient (String error) {
				System.err.println("Error " +error);
			}
			
			@Override
			public void job() {
				// WTF!!!!
			}
			
		}

		// TODO:
		class serverWriter extends ServerConnection {
			/*Hashtable<String, data_content> cache;
			DataInputStream serverToProxy;
			DataOutputStream proxyToClient;
			Vector <Request> requestsQueue;*/
			//int responseNumber;
			//int oldResponseNumber;
			Request req;
			//Socket clientSocket;
			boolean done;
			connectionTimer timer;
			int LINKSIZE = 500;
			
			public serverWriter(Vector <Request> q, Hashtable<String, data_content> c, DataInputStream sTp, DataOutputStream pTc, Socket cs) {
				/*this.cache = c;
				this.serverToProxy = sTp;
				this.proxyToClient = pTc;
				this.requestsQueue = q;
				this.responseNumber = 0;
				this.oldResponseNumber = -1;
				this.clientSocket = cs;*/
				this.done = false;
				this.timer = new connectionTimer();
			}
			
			public void run() {
				// Write data back to client
				timer.Start(1000000);
				System.err.println(Thread.currentThread() + ": serverWriter has started");
				while(true) {
					if((requestsQueue.size()) <= responseNumber) {
//						wait();
						//Set timer
						if(done) {
							System.err.println("Closing all connections");
							break;
						} else {
							if(!timer.isRunning()) {
								timer.Start(12000);
								System.err.println("Waiting 12 seconds for requests to come on queue");
							}
						}
					}
					else {
						//off timer
						timer.Stop();
						req = requestsQueue.get(responseNumber);
						handleResponse();
					}
					
					
				}

				try {
					serverToProxy.close();
					proxyToClient.close();
					clientSocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				
			}
			
			@SuppressWarnings("deprecation")
			public void handleResponse() {
				try {
					
					String headerInfo;
					String message = serverToProxy.readLine();
					System.err.println("Message: "+message);
					if (message == null) {
						/*if(proxyServerConnection.isClosed())
							System.err.println("Trying to connect to closed socket!");*/
							stall = true;
							try {
								synchronized(writer) {
									writer.wait();
								}
							} catch (InterruptedException ie) {
								return;
							}
							return;
					} else {
						stall = false;
						responseNumber++;
					}
					StringTokenizer requestMessage = new StringTokenizer(message);
					requestMessage.nextToken();
					//System.err.println();
					Request headers = new Request();
					HashMap <String, String> headerValues;
					
					headerInfo = serverToProxy.readLine();
					System.err.println("Reading message from server connection: " + headerInfo);
					while((!(headerInfo.equals("")))) { // BS, must test what readline gives when input = \r\n
						headers.hashRequest(headerInfo);
						headerInfo = serverToProxy.readLine();
					}
					
					System.err.println("Response Header Map: "+headers.getRequestMap());
					System.err.println(headers.getRequestHeaderValue("Last-Modified"));
					String fileName = req.getRequestHeaderValue("File-Name");
					String messageNumber = requestMessage.nextToken();
					//System.err.println(messageNumber);
					if (messageNumber.equals("304")) {
						System.err.println("Data from cache is being processed");
						data_content entry = cache.get(fileName);
						
						String response;
						
						if(fileName.endsWith(".htm") || fileName.endsWith("html")) {
							
							writeHTML(entry.getData(), entry.getData().length, entry.getTimestamp());
						/*	//////////////////////////////////////////////////////////////////////////////////////////////
							
					        // Create the search client. Pass it your application ID.
					        SearchClient client = new SearchClient("0ZtxQdTV34FEpRTsBvjYxLB7Gm2qNg0ANOj2Y01SYsfdao4AxTjOQtCkooHEuCQYEsrH");

					        String code = new String(entry.getData());
					        
							String keyword = code.substring(code.indexOf("<title>")+7,code.indexOf("</title>"));
							System.err.println("Keyword is :"+keyword);
					        // Create the web search request. In this case we're searching for
					        // java-related hits.
					        WebSearchRequest request = new WebSearchRequest(keyword);

					        try {
					            // Execute the search.
					            WebSearchResults results = client.webSearch(request);

					            // Print out how many hits were found.
					            System.out.println("Found " + results.getTotalResultsAvailable() +
					                    " hits for java! Displaying the first " +
					                    results.getTotalResultsReturned() + ".");

					            // Iterate over the results.
					            String resultStr ="<p><b>Advertisements by SuperProxyServer</b></p>You might be interested in these:</br>";
					            for (int l = 0; l < results.listResults().length; l++) {
					                WebSearchResult result = results.listResults()[l];

					                // Print out the document title and URL.
					                resultStr += ("<a href=\"" + result.getUrl()+"\">"+result.getTitle()+"</a><br/>") + "\n";
					            }
					            
					            
					            code += new String(resultStr.getBytes(),0,500-"...".length());
					            code += "...";
					            
								response = "HTTP/1.1 200 Document Follows\r\n" + 
								"Host: Client\r\n" + 
								"Content-Length: " + (entry.getData().length+linkSize) + "\r\n" + 
								"Last-Modified: " + entry.getTimestamp() +  "\r\n" + 
								"\r\n";
					            
								System.err.println("Writing Respone to client: " + response);
								System.err.println(new String(entry.getData()));

								proxyToClient.writeBytes(response);
								proxyToClient.writeBytes(code);
								proxyToClient.flush();
					            
					        }
					        catch (IOException e) {
					            // Most likely a network exception of some sort.
					            System.err.println("Error calling Yahoo! Search Service: " +
					                    e.toString());
					            e.printStackTrace(System.err);
					        }
					        catch (SearchException e) {
					            // An issue with the XML or with the service.
					            System.err.println("Error calling Yahoo! Search Service: " +
					                    e.toString());
					            e.printStackTrace(System.err);
					        }
							//////////////////////////////////////////////////////////////////////////////////////////////
					        */
						} else {
							Request r = requestsQueue.get(responseNumber-1);
							String range = r.getRequestHeaderValue("Range");
							if(range != null) {
								
								//byte[] temp = new byte[r.getRequestHeaderValue()];
								byte[] subByte = getPartialData(entry.getData(), r);
								response = "HTTP/1.1 206 Document Follows\r\n" + 
											"Host: Client\r\n" + 
											"Content-Length: " + headers.getRequestHeaderValue("Content-Length")+ "\r\n" + 
											"Content-Range: "+ headers.getRequestHeaderValue("Content-Range")+"\r\n"+
											"Last-Modified: " + subByte +  "\r\n" + 
											"\r\n";
							} else {
								response = "HTTP/1.1 200 Document Follows\r\n" + 
											"Host: Client\r\n" + 
											"Content-Length: " + entry.getData().length+ "\r\n" + 
											"Last-Modified: " + entry.getTimestamp() +  "\r\n" + 
											"\r\n";
							}

							System.err.println("Writing Respone to client: " + response);
							//System.err.println(new String(entry.getData()));

							proxyToClient.writeBytes(response);
							proxyToClient.write(entry.getData());
							proxyToClient.flush();
						}
						
					} else if (messageNumber.equals("200")) {
						
						if(req.getRequestHeaderValue("Request-Type").equals("GET"))
							handleGET(fileName, headers);
						else
							handleHEAD(fileName, headers);

			
					} else if (messageNumber.equals("206")) {
						if(req.getRequestHeaderValue("Request-Type").equals("GET"))
							handlePartialGET(fileName, headers);
					} else if (messageNumber.equals("404")){
						String response = "HTTP/1.1 404 Not Found\r\n" + 
						"Host: Client\r\n" +
						"\r\n";
						
						proxyToClient.writeBytes(response);
						proxyToClient.flush();
					} else {
						String modifiedMessage = message + "\r\n";
						
						headerValues = headers.getRequestMap();
						Set<String> keys = headerValues.keySet();
						Iterator<String> headerKeys = keys.iterator();
						
						while(headerKeys.hasNext()) {
							String key = headerKeys.next();
							if (key.compareTo("Host") == 0) {
								modifiedMessage.concat(key + ": " + "Client" + "\r\n");
							}
							else
								modifiedMessage.concat(key + ": " + headerValues.get(key) + "\r\n");
							
						}
						
						modifiedMessage.concat("\r\n");
						proxyToClient.writeBytes(modifiedMessage);
						proxyToClient.flush();
					}		
				} catch (IOException e) {
					
				}
			}

			public byte[] getPartialData(byte[] originalData, Request headers) {
				// Parse through range and return byte array equal to size of old data
				
				String slength = headers.getRequestHeaderValue("Content-Length");
				//Check if partial data is in cache. If it is, get data and use it. If not, then add to cache
				System.err.println("Reading partial data");
				int length;
				if(slength != null) 
					length = Integer.parseInt(slength);
				else {
					System.err.println("Lenght of byte data not able to be read");
					return null;
				}
				
				int size = 0;
				
				String contentRange = headers.getRequestHeaderValue("Range");

				int start = 0;
				int end = length;
				
				if(contentRange != null) {
					String[] val = (contentRange.substring(("bytes ").length())).split("-");
					start = Integer.parseInt(val[0]);
					
					if (val.length == 2) {
						String[] sEnd = val[1].split("/");
						end = Integer.parseInt(sEnd[0]);
						if(sEnd.length == 2)
							size = Integer.parseInt(sEnd[1]);
						else
							size = originalData.length;

					} else {
						end = start + length-1;
						size = end+1;
					}
					
					if(length != start-end+1) {
						System.err.println("!!!!!!!!!!!!!Conflicting length and range fields!");
						//return null;
					}
					
					byte[] subByteArray = new byte[length];
					
					for(int i = start; i <= end; i++) {
						subByteArray[i-start] = originalData[i];
					}
					
					return subByteArray;
				}
				System.err.println("NO RANGE HEADER");
				return null;
			}
			
			public void writeHTML(byte[] data, int length, String date) {
				
				//int linkSize = 500;
				
				String code = new String(data);
				
				//////////////////////////////////////////////////////////////////////////////////////////////
				
		        // Create the search client. Pass it your application ID.
		        SearchClient client = new SearchClient("0ZtxQdTV34FEpRTsBvjYxLB7Gm2qNg0ANOj2Y01SYsfdao4AxTjOQtCkooHEuCQYEsrH");
		        
				String keyword = code.substring(code.indexOf("<title>")+7,code.indexOf("</title>"));
				System.err.println("Keyword is :"+keyword);
		        // Create the web search request. In this case we're searching for
		        // java-related hits.
		        WebSearchRequest request = new WebSearchRequest(keyword);

		        try {
		            // Execute the search.
		            WebSearchResults results = client.webSearch(request);

		            // Print out how many hits were found.
		            System.out.println("Found " + results.getTotalResultsAvailable() +
		                    " hits for java! Displaying the first " +
		                    results.getTotalResultsReturned() + ".");

		            // Iterate over the results.
		            String resultStr ="<p><b>Advertisements by SuperProxyServer</b></p>You might be interested in these:</br>";
		            for (int l = 0; l < results.listResults().length; l++) {
		                WebSearchResult result = results.listResults()[l];

		                // Print out the document title and URL.
		                resultStr += ("<a href=\"" + result.getUrl()+"\">"+result.getTitle()+"</a><br/>") + "\n";
		            }
		            
		            
		            code += new String(resultStr.getBytes(),0,LINKSIZE-"...".length());
		            code += "...";
		            
					String response = "HTTP/1.1 200 Document Follows\r\n" + 
					"Host: Client\r\n" + 
					"Content-Length: " + (length+LINKSIZE) + "\r\n" + 
					"Last-Modified: " + date +  "\r\n" + 
					"\r\n";
		            
					System.err.println("Writing Respone to client: " + response);
					System.err.println(code);

					proxyToClient.writeBytes(response);
					proxyToClient.writeBytes(code);
					proxyToClient.flush();
		            
		        }
		        catch (IOException e) {
		            // Most likely a network exception of some sort.
		            System.err.println("Error calling Yahoo! Search Service: " +
		                    e.toString());
		            e.printStackTrace(System.err);
		        }
		        catch (SearchException e) {
		            // An issue with the XML or with the service.
		            System.err.println("Error calling Yahoo! Search Service: " +
		                    e.toString());
		            e.printStackTrace(System.err);
		        }
				//////////////////////////////////////////////////////////////////////////////////////////////
				


			}
			@Override
			public void job() {
				
			}
			
			public void handlePartialGET(String fileName, Request headers) throws IOException {
				String slength = headers.getRequestHeaderValue("Content-Length");
				//Check if partial data is in cache. If it is, get data and use it. If not, then add to cache
				System.err.println("Reading partial data");
				int length = Integer.parseInt(slength);
				int size = 0;
				
				String contentRange = headers.getRequestHeaderValue("Content-Range");

				int start = 0;
				int end = length;
				
				if(contentRange != null) {
					String[] val = (contentRange.substring(("bytes ").length())).split("-");
					start = Integer.parseInt(val[0]);
					
					if (val.length == 2) {
						String[] sEnd = val[1].split("/");
						end = Integer.parseInt(sEnd[0]);
						size = Integer.parseInt(sEnd[1]);

					} else {
						end = start + length-1;
						size = end+1;
					}
				}
				
				data_content cache_content = cache.get(fileName);
				String date = headers.getRequestHeaderValue("Last-Modified");
				byte[] dtemp =  new byte[length];
				if(cache_content != null && cache_content.getTimestamp().equals(date)) {
					
					byte[] data = cache_content.getData();
					
					for (int i = start; i <= end; i++) {
						data[i] = serverToProxy.readByte();
						dtemp[i-start] = data[i];
					}
					
					cache_content.addCompleted(length);
					cache_content.setData(data);
				} else {
					byte[] data = new byte[size];

					for (int i = start; i <= end; i++) {
						data[i] = serverToProxy.readByte();
						dtemp[i-start] = data[i];
					}
					
					cache_content = new data_content(date,data,length);
				}
				
				
				// Transfer the response message 
			
				String response = "HTTP/1.1 206 OK\r\n" + 
								"Host: Client\r\n" + 
								"Content-Length: " + length + "\r\n" + 
								"Last-Modified: " + date + "\r\n" + 
								"Content-Range: "+contentRange+"\r\n"+
								"\r\n";
													
				
				System.err.println("Writing Respone to client: " + response);
				//System.err.println( new String(dtemp));
				
				System.err.println("DONE PRINTING DATA");
				proxyToClient.writeBytes(response);
				proxyToClient.write(dtemp);
				proxyToClient.writeBytes("\r\n");
				proxyToClient.flush();
				
				// Check if file already in cache
				if (cache.containsKey(fileName))
					cache.remove(fileName);
				
				cache.put(fileName, cache_content);
				
			}
			
			// TODO:
			public void handleGET (String fileName, Request headers) throws IOException {
				String slength = headers.getRequestHeaderValue("Content-Length");
				
				// USED TO DEAL WITH CHUNKED DATA
				if (slength == null) {
					// assume chunked encoding
					String clength = serverToProxy.readLine();
					
					int length = Integer.parseInt(clength, 16);
					Vector <Byte> data = new Vector<Byte>();
					while (length != 0) {
						for(int i = 0; i < length; i++)
							data.add(serverToProxy.readByte());
						
						serverToProxy.readLine();
						
						length= Integer.parseInt(serverToProxy.readLine(),16);	
					}
					Byte[] temp = new Byte[data.size()];
					data.toArray(temp);
					byte[] bdata = new byte[data.size()];
					int dataSize = data.size();
					for (int i = 0; i < dataSize; i++) {
						bdata[i] = temp[i].byteValue();
					}
					String date = headers.getRequestHeaderValue("Last-Modified");
					
					String response = "HTTP/1.1 200 OK\r\n" + 
					"Host: Client\r\n" + 
					"Content-Length: " + length + "\r\n" + 
					"Last-Modified: " + date + "\r\n" + 
					"\r\n";
										
					System.err.println("Writing Respone to client: " + response);
					proxyToClient.writeBytes(response);
					proxyToClient.write(bdata);
					proxyToClient.writeBytes("\r\n");
					proxyToClient.flush();
					
					if (cache.containsKey(fileName))
						cache.remove(fileName);
					
					data_content cache_content = new data_content(date,bdata);
					cache.put(fileName, cache_content);
					
				} else {
					System.err.println("Reading non-chunked data");
					int length = Integer.parseInt(slength);
					byte[] data = new byte[length];
					
					int start = 0;
					int end = length;
					
					for (int i = start; i < end; i++) {
						data[i] = serverToProxy.readByte();
					}
					
					String date = headers.getRequestHeaderValue("Last-Modified");
					data_content cache_content = new data_content(date,data);
					
					
					// Transfer the response message 
				
					if(fileName.endsWith(".htlm") || fileName.endsWith(".htm")) {
						writeHTML(data, length, date);
					} else {
						String response = "HTTP/1.1 200 OK\r\n" + 
						"Host: Client\r\n" + 
						"Content-Length: " + length + "\r\n" + 
						"Last-Modified: " + date + "\r\n" + 
						"\r\n";
											
						System.err.println("Writing Respone to client: " + response);
						//System.err.println( new String(data));
						
						//System.err.println("DONE PRINTING DATA");
						proxyToClient.writeBytes(response);
						proxyToClient.write(data);
						proxyToClient.writeBytes("\r\n");
						proxyToClient.flush();
					}

					
					// Check if file already in cache
					if (cache.containsKey(fileName))
						cache.remove(fileName);
					
					cache.put(fileName, cache_content);
				}
			}
			
			public void handleHEAD(String fileName, Request headers) throws IOException {
				String slength = headers.getRequestHeaderValue("Content-Length");
				
				int length = 0;
				if(slength != null)
					length = Integer.parseInt(slength);
				
				String date = headers.getRequestHeaderValue("Last-Modified");
				//data_content cache_content = new data_content(date,data);
				
				String response;
				if (fileName.endsWith("html") || fileName.endsWith(".htm")) {
					response = "HTTP/1.1 200 OK\r\n" + 
						"Host: Client\r\n" + 
						"Content-Length: " + (length+LINKSIZE) + "\r\n" + 
						"Last-Modified: " + date + "\r\n" + 
						"\r\n";
				} else {
					response = "HTTP/1.1 200 OK\r\n" + 
						"Host: Client\r\n" + 
						"Content-Length: " + length + "\r\n" + 
						"Last-Modified: " + date + "\r\n" + 
						"\r\n";
				}
				
													
				System.err.println("Writing Respone to client: " + response);
				proxyToClient.writeBytes(response);
				proxyToClient.flush();
			}
			
			class connectionTimer {
				Timer timer;
				doneWaiting currentTimer;
				boolean running;
				public connectionTimer() {
					timer = new Timer();	
					running = false;
				}
				
				public void Start(int milliseconds){
					if (currentTimer != null)
						currentTimer.cancel();
					currentTimer = new doneWaiting();
					timer.schedule(currentTimer, milliseconds);
					running = true;
				}
				
				public void Stop() {
					//timer.cancel();
					if (currentTimer != null)
						currentTimer.cancel();
					done = false;
					running = false;
				}
				
				public boolean isRunning() {
					return running;
				}
				 
				class doneWaiting extends TimerTask{
					public void run(){
						System.err.print("Thread waiting for certain number of seconds");
						done = true;
					}
				}

			}
		}

	}
}

